Skip to content

[TOOL-4531] Dashboard: Add Token Asset creation wizard #7081

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

MananTank
Copy link
Member

@MananTank MananTank commented May 19, 2025


PR-Codex overview

This PR focuses on enhancing the asset creation workflow in the application, particularly for deploying and managing ERC-20 tokens. It introduces new tracking functionalities, UI components, and state management improvements.

Detailed summary

  • Added deploymentType and contractType properties in various components.
  • Introduced revalidatePathAction for cache revalidation.
  • Updated UI components for improved asset management.
  • Enhanced forms with validation for token creation.
  • Implemented multi-step status tracking for token deployment.
  • Added new components for token distribution and sales setup.
  • Improved error handling and user feedback during operations.

The following files were skipped due to too many changes: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-airdrop.tsx

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive asset management interface for creating, importing, and managing ERC-20 tokens within team and project dashboards.
    • Added a multi-step token creation flow with form validation, including token info, distribution, sale settings, airdrop CSV upload, and launch process with real-time status tracking.
    • Implemented visual token allocation charts and multi-step progress indicators for enhanced user experience.
    • Added a dedicated "Assets" section in the sidebar with a "New" badge.
    • Added a horizontal segmented distribution bar chart component for visualizing token allocations.
    • Introduced a multi-step status component to track asynchronous step executions with retry capabilities.
    • Added token airdrop CSV upload and validation feature with user-friendly UI and error handling.
    • Added token sale configuration section with price and allocation inputs.
    • Enhanced import modal to support both contract and asset imports with contextual UI and behavior.
    • Added new pages and components to support token asset creation and management workflows, including cards for asset actions.
    • Added server-side path revalidation support for pages and layouts.
  • Improvements

    • Enhanced contract and asset import workflows with clearer UI, context-aware dialogs, and improved contract table variant handling.
    • Updated number input fields to remove browser-native spinners for a cleaner appearance.
    • Improved analytics event tracking for user interactions and deployment steps.
    • Refined UI components for consistent styling and accessibility.
    • Fixed sidebar badge placement and updated UI text for clarity.
    • Updated dialogs and payment embeds to reference transaction chain instead of active wallet chain and set default payment amounts.
    • Changed token supply label to "Circulating Supply" for clarity.
    • Replaced Chakra UI spinner and styled elements with local components for simplified UI.
  • Bug Fixes

    • Corrected UI typos and improved analytics event tracking for deployment errors.
  • Documentation

    • Added Storybook stories for new components to facilitate UI testing and documentation.
  • Chores

    • Refactored and extended types and props to support new asset and contract features, ensuring future extensibility.
    • Added tracking utilities for token deployment and asset creation analytics.

Copy link

linear bot commented May 19, 2025

Copy link

vercel bot commented May 19, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
thirdweb-www ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 22, 2025 11:23pm
4 Skipped Deployments
Name Status Preview Comments Updated (UTC)
docs-v2 ⬜️ Skipped (Inspect) May 22, 2025 11:23pm
login ⬜️ Skipped (Inspect) May 22, 2025 11:23pm
thirdweb_playground ⬜️ Skipped (Inspect) May 22, 2025 11:23pm
wallet-ui ⬜️ Skipped (Inspect) May 22, 2025 11:23pm

@vercel vercel bot temporarily deployed to Preview – thirdweb_playground May 19, 2025 19:54 Inactive
@vercel vercel bot temporarily deployed to Preview – wallet-ui May 19, 2025 19:54 Inactive
@vercel vercel bot temporarily deployed to Preview – docs-v2 May 19, 2025 19:54 Inactive
@vercel vercel bot temporarily deployed to Preview – login May 19, 2025 19:54 Inactive
Copy link

changeset-bot bot commented May 19, 2025

⚠️ No Changeset found

Latest commit: 73629e9

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@MananTank MananTank marked this pull request as ready for review May 19, 2025 19:54
@MananTank MananTank requested review from a team as code owners May 19, 2025 19:54
@github-actions github-actions bot added the Dashboard Involves changes to the Dashboard. label May 19, 2025
Copy link
Member Author

MananTank commented May 19, 2025


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge-queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

Copy link

codecov bot commented May 19, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 55.61%. Comparing base (23029db) to head (73629e9).
Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #7081   +/-   ##
=======================================
  Coverage   55.61%   55.61%           
=======================================
  Files         902      902           
  Lines       58177    58177           
  Branches     4085     4085           
=======================================
  Hits        32356    32356           
  Misses      25716    25716           
  Partials      105      105           
Flag Coverage Δ
packages 55.61% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

github-actions bot commented May 19, 2025

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
thirdweb (esm) 56.62 KB (0%) 1.2 s (0%) 145 ms (+110.5% 🔺) 1.3 s
thirdweb (cjs) 309.13 KB (0%) 6.2 s (0%) 575 ms (+0.73% 🔺) 6.8 s
thirdweb (minimal + tree-shaking) 5.69 KB (0%) 114 ms (0%) 38 ms (+655.69% 🔺) 152 ms
thirdweb/chains (tree-shaking) 531 B (0%) 11 ms (0%) 21 ms (+1294.15% 🔺) 32 ms
thirdweb/react (minimal + tree-shaking) 19.5 KB (0%) 390 ms (0%) 73 ms (+438.93% 🔺) 463 ms

@MananTank MananTank force-pushed the 05-20-_tool-4531_dashboard_add_token_asset_creation_wizard branch from 7f8c64d to c37589e Compare May 19, 2025 20:23
@vercel vercel bot temporarily deployed to Preview – docs-v2 May 19, 2025 20:23 Inactive
@vercel vercel bot temporarily deployed to Preview – login May 19, 2025 20:23 Inactive
@vercel vercel bot temporarily deployed to Preview – wallet-ui May 19, 2025 20:23 Inactive
@vercel vercel bot temporarily deployed to Preview – thirdweb_playground May 19, 2025 20:23 Inactive
@MananTank MananTank force-pushed the 05-20-_tool-4531_dashboard_add_token_asset_creation_wizard branch from c37589e to dbe4965 Compare May 19, 2025 20:30
@vercel vercel bot temporarily deployed to Preview – docs-v2 May 19, 2025 20:30 Inactive
@vercel vercel bot temporarily deployed to Preview – wallet-ui May 19, 2025 20:30 Inactive
@vercel vercel bot temporarily deployed to Preview – login May 19, 2025 20:30 Inactive
@vercel vercel bot temporarily deployed to Preview – thirdweb_playground May 19, 2025 20:30 Inactive
@vercel vercel bot temporarily deployed to Preview – wallet-ui May 22, 2025 22:29 Inactive
@vercel vercel bot temporarily deployed to Preview – thirdweb_playground May 22, 2025 22:29 Inactive
@vercel vercel bot temporarily deployed to Preview – docs-v2 May 22, 2025 22:29 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🔭 Outside diff range comments (1)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx (1)

367-390: ⚠️ Potential issue

Prevent memory leaks by revoking object URLs

The URL.createObjectURL call in the download button creates a unique object URL that should be revoked when no longer needed to prevent memory leaks.

function DownloadExampleCSVButton() {
  return (
    <Button
      size="sm"
      onClick={() => {
        const link = document.createElement("a");
        const exampleData = [
          {
            address: "0x000000000000000000000000000000000000dEaD",
            quantity: "2",
          },
          {
            address: "thirdweb.eth",
            quantity: "1",
          },
        ];

        const csv = `address,quantity\n${exampleData
          .map((o) => `${o.address},${o.quantity}`)
          .join("\n")}`;

        const blob = new Blob([csv], { type: "text/csv" });
        link.href = URL.createObjectURL(blob);
        link.download = "airdrop.csv";
        link.click();
+       // Clean up the URL object to prevent memory leaks
+       setTimeout(() => {
+         URL.revokeObjectURL(link.href);
+       }, 100);
      }}
    >
      <ArrowDownToLineIcon className="mr-2 size-4" />
      Download Example CSV
    </Button>
  );
}
♻️ Duplicate comments (9)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/page.tsx (1)

62-69: Add error boundary to complement Suspense

The Suspense component is used without a complementary error boundary, which could lead to unhandled promise rejections.

Consider wrapping the Suspense content with an ErrorBoundary component to gracefully handle errors:

import { ErrorBoundary } from '@/components/error-boundary'; // Use your preferred error boundary

<ErrorBoundary fallback={<div>Something went wrong. Please try again.</div>}>
  <Suspense fallback={<GenericLoadingPage />}>
    <AssetsPageAsync
      teamId={team.id}
      projectId={project.id}
      authToken={authToken}
      client={client}
    />
  </Suspense>
</ErrorBoundary>
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx (2)

83-85: 🛠️ Refactor suggestion

Add form validation before proceeding to next step

Currently, the code transitions to the next step without validating that the current form is valid. This could lead to incomplete or invalid data being used later in the flow.

onNext={() => {
+   // Validate form before proceeding
+   tokenInfoForm.trigger().then((isValid) => {
+     if (isValid) {
        setStep("distribution");
+     }
+   });
}}

97-99: 🛠️ Refactor suggestion

Add form validation before proceeding to launch step

Similar to the previous step transition, there's no validation before moving to the launch step.

onNext={() => {
+   // Validate form before proceeding
+   tokenDistributionForm.trigger().then((isValid) => {
+     if (isValid) {
        setStep("launch");
+     }
+   });
}}
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-airdrop.tsx (3)

53-56: ⚠️ Potential issue

Guard against non-numeric quantities to prevent NaN in totals

Number(curr.quantity) will produce NaN for invalid or empty strings, causing the whole sum to become NaN.

-  const totalAirdropSupply = airdropAddresses.reduce(
-    (acc, curr) => acc + Number(curr.quantity),
-    0,
-  );
+  const totalAirdropSupply = airdropAddresses.reduce((acc, curr) => {
+    const qty = Number(curr.quantity);
+    return acc + (Number.isFinite(qty) ? qty : 0);
+  }, 0);

430-430: 🛠️ Refactor suggestion

Add validation for quantity values

The table correctly displays validation for invalid addresses but doesn't indicate or validate if quantities are non-numeric, which could lead to NaN values in calculations.

-              <TableCell>{item.quantity}</TableCell>
+              <TableCell>
+                {!isNaN(Number(item.quantity)) ? (
+                  item.quantity
+                ) : (
+                  <div className="flex flex-row items-center gap-2">
+                    <CircleAlertIcon className="size-4 text-red-500" />
+                    <span className="font-bold text-red-500">
+                      {item.quantity}
+                    </span>
+                  </div>
+                )}
+              </TableCell>

193-200: 🛠️ Refactor suggestion

Validate quantity values during CSV parsing

The CSV parser doesn't validate if quantities are valid numbers, which could lead to issues later in calculations.

// CSV parser for airdrop data
const csvParser = (items: AirdropAddressInput[]): AirdropAddressInput[] => {
  return items
    .map(({ address, quantity }) => {
+     const trimmedQuantity = (quantity || "1").trim();
+     // Check if quantity is a valid number
+     const isValidQuantity = !isNaN(Number(trimmedQuantity)) && Number(trimmedQuantity) > 0;
      return {
        address: (address || "").trim(),
-       quantity: (quantity || "1").trim(),
+       quantity: isValidQuantity ? trimmedQuantity : "1",
+       hasInvalidQuantity: !isValidQuantity && quantity !== undefined,
      };
    })
    .filter(({ address }) => address !== "");
};
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx (3)

48-57: Add error handling for file uploads.

The FileInput component handles file selection but doesn't appear to have error handling for common upload issues like file size limits or format validation failures beyond the accept prop.

Consider adding explicit error handling:

<FileInput
  accept={{ "image/*": [] }}
  value={form.watch("image")}
  setValue={(file) =>
    form.setValue("image", file, {
      shouldTouch: true,
    })
  }
+ maxSizeInBytes={5 * 1024 * 1024} // Example: 5MB limit
+ onError={(error) => {
+   form.setError("image", { 
+     type: "manual", 
+     message: error.message 
+   });
+ }}
  className="rounded-lg border-border bg-background transition-all duration-200 hover:border-active-border hover:bg-background"
/>

100-110: Handle chain data load errors and add selection validation

Currently, useAllChainsData swallows any fetch errors and SingleNetworkSelector simply stays disabled (showing "Loading Chains…") if allChains is empty—on a failed network request it never recovers. Also, there's no form‐level feedback if the user never picks a chain.

Suggested fixes:

• In apps/dashboard/src/hooks/chains/allChains.ts
– Return isLoading, isError, and error from the underlying useQuery (allChainsQuery) in addition to populating the store.
• In apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx (around lines 127–134)
– Accept/loading and error flags and pass them down to SelectWithSearch.
– Render:
– a loading spinner or "Loading chains…" while isLoading
– an inline error message (e.g. "Failed to load networks. Please retry") with a retry handler when isError
• In apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx (lines 100–110)
– Before rendering SingleNetworkSelector, check for and display any loading/error state exposed by the hook.
– Add form validation for chain (e.g. required) and display a validation error if the user submits without selecting a chain.


176-191: Add URL validation for social URLs.

The social URL field should validate that the input is a properly formatted URL.

Consider using a pattern or custom validation in your form schema for the URL field:

<FormField
  control={form.control}
  name={`socialUrls.${index}.url`}
  render={({ field }) => (
    <FormItem className="flex-1">
      <FormControl>
        <Input
          {...field}
          placeholder="https://..."
          aria-label="Platform URL"
+         type="url"
        />
      </FormControl>
      <FormMessage />
    </FormItem>
  )}
/>

Also ensure your form schema (in form.ts) includes URL validation for these fields.

🧹 Nitpick comments (6)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx (6)

6-6: Remove empty import statement.

There's an unnecessary empty import from the card component that should be removed.

-import {} from "@/components/ui/card";

50-55: Optimize form performance with controlled inputs.

Using form.watch() for the file input value can cause unnecessary re-renders. Consider refactoring to use a controlled component pattern to improve performance.

<FileInput
  accept={{ "image/*": [] }}
- value={form.watch("image")}
+ value={form.getValues("image")}
  setValue={(file) =>
    form.setValue("image", file, {
      shouldTouch: true,
    })
  }
  className="rounded-lg border-border bg-background transition-all duration-200 hover:border-active-border hover:bg-background"
/>

104-107: Optimize chain selection to avoid unnecessary re-renders.

Similar to the file input, using form.watch("chain") can cause unnecessary re-renders. Consider using a more efficient approach:

<SingleNetworkSelector
  className="bg-background"
  client={props.client}
- chainId={Number(form.watch("chain"))}
+ chainId={Number(form.getValues("chain"))}
  onChange={(chain) => {
    form.setValue("chain", chain.toString());
+   form.trigger("chain"); // Trigger validation
  }}
  disableChainId
/>

149-149: Improve accessibility with proper heading hierarchy.

The social URLs section uses an h2 heading, but it's not clear if this follows the proper heading hierarchy of the page. Ensure heading levels are used properly for accessibility.

- <h2 className="mb-2 font-medium text-sm">Social URLs</h2>
+ <h3 className="mb-2 font-medium text-sm">Social URLs</h3>

32-40: Add form error boundary.

Consider adding an error boundary around your form to gracefully handle any unexpected rendering errors.

<Form {...form}>
  <form onSubmit={form.handleSubmit(props.onNext)}>
+   <ErrorBoundary fallback={<div>Something went wrong with the form. Please try again or contact support.</div>}>
      <StepCard
        page="info"
        title="Token Information"
        prevButton={undefined}
        nextButton={{
          type: "submit",
        }}
      >
+   </ErrorBoundary>

212-212: Add default values when adding new social URLs.

When adding a new social URL, consider providing default platform values like "Twitter" or "Discord" to make it easier for users:

-  onClick={() => append({ platform: "", url: "" })}
+  onClick={() => {
+    // If no platforms exist yet, suggest common ones
+    const existingPlatforms = form.getValues("socialUrls").map(item => item.platform.toLowerCase());
+    let defaultPlatform = "";
+    
+    if (!existingPlatforms.includes("twitter") && !existingPlatforms.includes("x")) {
+      defaultPlatform = "Twitter";
+    } else if (!existingPlatforms.includes("discord")) {
+      defaultPlatform = "Discord";
+    } else if (!existingPlatforms.includes("website")) {
+      defaultPlatform = "Website";
+    }
+    
+    append({ platform: defaultPlatform, url: "" });
+  }}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b55a2b and 7e90eb0.

📒 Files selected for processing (36)
  • apps/dashboard/src/@/actions/revalidate.ts (1 hunks)
  • apps/dashboard/src/@/components/blocks/distribution-chart.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.stories.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/_components/DeployedContractsPage.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/_components/getProjectContracts.ts (1 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/_components/getSortedDeployedContracts.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/cards.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-card.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page-impl.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.stories.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-airdrop.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-distribution.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-sale.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/form.ts (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/tracking.ts (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/components/ProjectSidebarLayout.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/connect/account-abstraction/factories/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/hooks/project-contracts.ts (1 hunks)
  • apps/dashboard/src/components/buttons/MismatchButton.tsx (2 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/import-contract/modal.tsx (7 hunks)
  • apps/dashboard/src/components/contract-components/tables/contract-table.stories.tsx (6 hunks)
  • apps/dashboard/src/components/contract-components/tables/contract-table.tsx (6 hunks)
  • apps/dashboard/src/contract-ui/components/solidity-inputs/address-input.tsx (3 hunks)
  • apps/dashboard/src/global.css (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (31)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx
  • apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/connect/account-abstraction/factories/page.tsx
  • apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
  • apps/dashboard/src/app/(app)/account/contracts/_components/DeployedContractsPage.tsx
  • apps/dashboard/src/global.css
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/components/ProjectSidebarLayout.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/hooks/project-contracts.ts
  • apps/dashboard/src/@/actions/revalidate.ts
  • apps/dashboard/src/components/contract-components/tables/contract-table.stories.tsx
  • apps/dashboard/src/contract-ui/components/solidity-inputs/address-input.tsx
  • apps/dashboard/src/app/(app)/account/contracts/_components/getProjectContracts.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx
  • apps/dashboard/src/components/buttons/MismatchButton.tsx
  • apps/dashboard/src/app/(app)/account/contracts/_components/getSortedDeployedContracts.tsx
  • apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.stories.tsx
  • apps/dashboard/src/components/contract-components/tables/contract-table.tsx
  • apps/dashboard/src/@/components/blocks/distribution-chart.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.stories.tsx
  • apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-card.tsx
  • apps/dashboard/src/components/contract-components/import-contract/modal.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-distribution.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-sale.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/page.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/cards.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/tracking.ts
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page-impl.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/form.ts
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Size
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Lint Packages
  • GitHub Check: Analyze (javascript)

Comment on lines +210 to +213
<Dialog
open={isModalOpen}
// do not set onOpenChange
>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Prevent accidental dialog dismissal

The comment on line 212 indicates that onOpenChange should not be set, but omitting it entirely allows the dialog to be closed by clicking outside or pressing escape.

<Dialog
  open={isModalOpen}
- // do not set onOpenChange
+ onOpenChange={(open) => {
+   // Only allow closing through the Close button
+   if (open === false) {
+     // Don't allow closing from outside clicks or close icon
+     return;
+   }
+   setIsModalOpen(open);
+ }}
>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Dialog
open={isModalOpen}
// do not set onOpenChange
>
<Dialog
open={isModalOpen}
onOpenChange={(open) => {
// Only allow closing through the Close button
if (open === false) {
// Don't allow closing from outside clicks or close icon
return;
}
setIsModalOpen(open);
}}
>
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx
around lines 210 to 213, the Dialog component lacks an onOpenChange handler,
which causes the dialog to be dismissible by clicking outside or pressing
escape. To prevent accidental dismissal, explicitly set onOpenChange to a no-op
function or a handler that prevents closing, ensuring the dialog remains open
unless explicitly closed by your intended controls.

Comment on lines +89 to +92
updateStatus(index, "error");
launchTracking("error");
console.error(error);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve error handling by capturing and displaying error details

When errors occur during token deployment, the current implementation only logs to the console without showing detailed feedback to users.

function createSequenceExecutorFn(
  index: number,
  executeFn: (values: CreateAssetFormValues) => Promise<void>,
) {
  return async () => {
    updateStatus(index, "pending");
    try {
      await executeFn(formValues);
      updateStatus(index, "completed");
      // start next one
      const nextStep = initialSteps[index + 1];
      if (nextStep) {
        // do not use await next step
        nextStep.execute();
      } else {
        launchTracking("success");
        props.onLaunchSuccess();
      }
    } catch (error) {
-     updateStatus(index, "error");
+     // Extract error message for display
+     const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
+     setSteps((prev) => {
+       return [
+         ...prev.slice(0, index),
+         { ...prev[index], status: "error", errorMessage },
+         ...prev.slice(index + 1),
+       ] as MultiStepState[];
+     });
      launchTracking("error");
      console.error(error);
    }
  };
}

You'll also need to update the MultiStepState type to include the optional errorMessage property and modify the MultiStepStatus component to display this message.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
updateStatus(index, "error");
launchTracking("error");
console.error(error);
}
function createSequenceExecutorFn(
index: number,
executeFn: (values: CreateAssetFormValues) => Promise<void>,
) {
return async () => {
updateStatus(index, "pending");
try {
await executeFn(formValues);
updateStatus(index, "completed");
// start next one
const nextStep = initialSteps[index + 1];
if (nextStep) {
// do not use await next step
nextStep.execute();
} else {
launchTracking("success");
props.onLaunchSuccess();
}
} catch (error) {
// Extract error message for display
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
setSteps((prev) => {
return [
...prev.slice(0, index),
{ ...prev[index], status: "error", errorMessage },
...prev.slice(index + 1),
] as MultiStepState[];
});
launchTracking("error");
console.error(error);
}
};
}
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx
around lines 89 to 92, improve error handling by capturing the error message and
updating the state with an errorMessage property. Modify the MultiStepState type
to include an optional errorMessage field, update the updateStatus call to pass
this message, and adjust the MultiStepStatus component to display the
errorMessage to users for better feedback.

Comment on lines +111 to +114
values={{
...tokenInfoForm.getValues(),
...tokenDistributionForm.getValues(),
}}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Validate combined form data before passing to LaunchTokenStatus

When combining form data for the launch step, no validation is performed to ensure the complete dataset is valid.

{step === "launch" && (
  <LaunchTokenStatus
    client={props.client}
    onLaunchSuccess={props.onLaunchSuccess}
    onPrevious={() => {
      setStep("distribution");
    }}
    createTokenFunctions={props.createTokenFunctions}
+   // Ensure both forms are valid before proceeding
+   isValid={tokenInfoForm.formState.isValid && tokenDistributionForm.formState.isValid}
+   form={{
+     tokenInfoForm,
+     tokenDistributionForm,
+   }}
    values={{
      ...tokenInfoForm.getValues(),
      ...tokenDistributionForm.getValues(),
    }}
  />
)}

Then in LaunchTokenStatus component, add validation before beginning the launch sequence:

// In LaunchTokenStatus component
async function handleSubmitClick() {
  // Re-validate both forms before proceeding
  const isTokenInfoValid = await props.form.tokenInfoForm.trigger();
  const isDistributionValid = await props.form.tokenDistributionForm.trigger();
  
  if (!isTokenInfoValid || !isDistributionValid) {
    console.log("Forms are not valid");
    return;
  }
  
  // Continue with launch process...
}
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx
around lines 111 to 114, the combined form data is passed to LaunchTokenStatus
without validation. To fix this, add validation logic inside the
LaunchTokenStatus component before starting the launch sequence by triggering
validation on both tokenInfoForm and tokenDistributionForm. If either form is
invalid, prevent the launch process from continuing and handle the invalid state
appropriately.

Comment on lines +31 to +32
<Form {...form}>
<form onSubmit={form.handleSubmit(props.onNext)}>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve form submission handling.

The form submission directly calls the onNext function without additional error handling or user feedback. Consider adding a loading state and error handling during form submission.

<Form {...form}>
-  <form onSubmit={form.handleSubmit(props.onNext)}>
+  <form onSubmit={form.handleSubmit(
+    (data) => {
+      try {
+        props.onNext();
+      } catch (error) {
+        console.error('Error in form submission:', error);
+        // Display error to user
+      }
+    },
+    (errors) => {
+      console.log('Validation errors:', errors);
+      // Optionally scroll to first error
+    }
+  )}>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Form {...form}>
<form onSubmit={form.handleSubmit(props.onNext)}>
<Form {...form}>
<form onSubmit={form.handleSubmit(
(data) => {
try {
props.onNext();
} catch (error) {
console.error('Error in form submission:', error);
// Display error to user
}
},
(errors) => {
console.log('Validation errors:', errors);
// Optionally scroll to first error
}
)}>
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx
around lines 31 to 32, the form submission calls onNext directly without
managing loading or errors. To fix this, implement a loading state that
activates when the form is submitted and deactivates after completion or error.
Add try-catch around the submission logic to handle errors gracefully and
provide user feedback, such as displaying error messages or disabling the submit
button while loading.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (7)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx (3)

83-85: 🛠️ Refactor suggestion

Add form validation before proceeding to next step.

Currently, the code transitions to the next step without validating that the current form is valid. This could lead to incomplete or invalid data being used later in the flow.

onNext={() => {
+   // Validate form before proceeding
+   tokenInfoForm.trigger().then((isValid) => {
+     if (isValid) {
        setStep("distribution");
+     }
+   });
}}

97-99: 🛠️ Refactor suggestion

Add form validation before proceeding to launch step.

Similar to the previous step transition, there's no validation before moving to the launch step.

onNext={() => {
+   // Validate form before proceeding
+   tokenDistributionForm.trigger().then((isValid) => {
+     if (isValid) {
        setStep("launch");
+     }
+   });
}}

111-114: 🛠️ Refactor suggestion

Validate combined form data before passing to LaunchTokenStatus.

When combining form data for the launch step, no validation is performed to ensure the complete dataset is valid.

{step === "launch" && (
  <LaunchTokenStatus
    client={props.client}
    onLaunchSuccess={props.onLaunchSuccess}
    onPrevious={() => {
      setStep("distribution");
    }}
    createTokenFunctions={props.createTokenFunctions}
+   // Ensure both forms are valid before proceeding
+   isValid={tokenInfoForm.formState.isValid && tokenDistributionForm.formState.isValid}
+   form={{
+     tokenInfoForm,
+     tokenDistributionForm,
+   }}
    values={{
      ...tokenInfoForm.getValues(),
      ...tokenDistributionForm.getValues(),
    }}
  />
)}
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx (4)

31-32: Improve form submission handling.

The form submission directly calls the onNext function without additional error handling or user feedback. Consider adding a loading state and error handling during form submission.

<Form {...form}>
-  <form onSubmit={form.handleSubmit(props.onNext)}>
+  <form onSubmit={form.handleSubmit(
+    (data) => {
+      try {
+        props.onNext();
+      } catch (error) {
+        console.error('Error in form submission:', error);
+        // Display error to user
+      }
+    },
+    (errors) => {
+      console.log('Validation errors:', errors);
+      // Optionally scroll to first error
+    }
+  )}>

100-110: Handle chain data load errors and add selection validation.

Currently, there's no explicit handling for potential network errors or failed chain data loading in the SingleNetworkSelector component.

The SingleNetworkSelector component should handle error states from chain data fetching, and the form should validate chain selection. Consider:

  1. Updating useAllChainsData to return isLoading, isError, and error flags
  2. Modifying SingleNetworkSelector to accept these flags and render appropriate loading/error states
  3. Adding form validation to require chain selection
#!/bin/bash
# Check current implementation of SingleNetworkSelector error handling
rg -n "export function SingleNetworkSelector" -A10 -B10 apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx

48-57: 🛠️ Refactor suggestion

Add error handling for file uploads.

The FileInput component handles file selection but doesn't appear to have error handling for common upload issues like file size limits or format validation failures beyond the accept prop.

<FileInput
  accept={{ "image/*": [] }}
  value={form.watch("image")}
  setValue={(file) =>
    form.setValue("image", file, {
      shouldTouch: true,
    })
  }
+ maxSizeInBytes={5 * 1024 * 1024} // Example: 5MB limit
+ onError={(error) => {
+   form.setError("image", { 
+     type: "manual", 
+     message: error.message 
+   });
+ }}
  className="rounded-lg border-border bg-background transition-all duration-200 hover:border-active-border hover:bg-background"
/>

176-191: 🛠️ Refactor suggestion

Add URL validation for social URLs.

The social URL field should validate that the input is a properly formatted URL.

<FormField
  control={form.control}
  name={`socialUrls.${index}.url`}
  render={({ field }) => (
    <FormItem className="flex-1">
      <FormControl>
        <Input
          {...field}
          placeholder="https://..."
          aria-label="Platform URL"
+         type="url"
        />
      </FormControl>
      <FormMessage />
    </FormItem>
  )}
/>

Also ensure your form schema (in form.ts) includes URL validation for these fields.

🧹 Nitpick comments (2)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx (1)

3-6: Remove unused empty imports.

There are several empty imports that should be removed as they add unnecessary code.

-import {} from "@/components/blocks/multi-step-status/multi-step-status";
-import {} from "@/components/ui/dialog";
 import { zodResolver } from "@hookform/resolvers/zod";
-import {} from "lucide-react";
 import { useState } from "react";
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx (1)

6-6: Remove unused empty import.

The card component is imported but not used. Remove this empty import.

-import {} from "@/components/ui/card";
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7e90eb0 and 24a556b.

📒 Files selected for processing (36)
  • apps/dashboard/src/@/actions/revalidate.ts (1 hunks)
  • apps/dashboard/src/@/components/blocks/distribution-chart.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.stories.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/_components/DeployedContractsPage.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/_components/getProjectContracts.ts (1 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/_components/getSortedDeployedContracts.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/cards.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-card.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page-impl.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.stories.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-airdrop.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-distribution.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-sale.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/form.ts (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/tracking.ts (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/components/ProjectSidebarLayout.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/connect/account-abstraction/factories/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/hooks/project-contracts.ts (1 hunks)
  • apps/dashboard/src/components/buttons/MismatchButton.tsx (2 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/import-contract/modal.tsx (7 hunks)
  • apps/dashboard/src/components/contract-components/tables/contract-table.stories.tsx (6 hunks)
  • apps/dashboard/src/components/contract-components/tables/contract-table.tsx (6 hunks)
  • apps/dashboard/src/contract-ui/components/solidity-inputs/address-input.tsx (3 hunks)
  • apps/dashboard/src/global.css (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.tsx
🚧 Files skipped from review as they are similar to previous changes (33)
  • apps/dashboard/src/app/(app)/account/contracts/_components/DeployedContractsPage.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx
  • apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
  • apps/dashboard/src/global.css
  • apps/dashboard/src/components/contract-components/tables/contract-table.stories.tsx
  • apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/connect/account-abstraction/factories/page.tsx
  • apps/dashboard/src/app/(app)/account/contracts/_components/getSortedDeployedContracts.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/components/ProjectSidebarLayout.tsx
  • apps/dashboard/src/@/actions/revalidate.ts
  • apps/dashboard/src/app/(app)/account/contracts/_components/getProjectContracts.ts
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/hooks/project-contracts.ts
  • apps/dashboard/src/contract-ui/components/solidity-inputs/address-input.tsx
  • apps/dashboard/src/components/buttons/MismatchButton.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/page.tsx
  • apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.stories.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/page.tsx
  • apps/dashboard/src/@/components/blocks/distribution-chart.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-card.tsx
  • apps/dashboard/src/components/contract-components/tables/contract-table.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/tracking.ts
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.stories.tsx
  • apps/dashboard/src/components/contract-components/import-contract/modal.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/cards.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-distribution.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-sale.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/form.ts
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-airdrop.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page-impl.tsx
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Lint Packages
  • GitHub Check: Size
  • GitHub Check: Analyze (javascript)

Copy link
Contributor

graphite-app bot commented May 22, 2025

Merge activity

<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces several enhancements and features related to asset management, including deployment tracking, UI updates, and new forms for creating and managing tokens. It also refines existing components and adds new functionalities for improved user experience.

### Detailed summary
- Added `deploymentType` and `contractType` properties in various components.
- Introduced `revalidatePathAction` for path revalidation.
- Updated UI components for better asset management.
- Enhanced forms for token creation with validation.
- Implemented multi-step status tracking for token launch processes.
- Improved error handling and user feedback in forms.
- Added new charts for token distribution visualization.

> The following files were skipped due to too many changes: `apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx`, `apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-airdrop.tsx`

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Introduced a comprehensive asset management interface for creating, importing, and managing ERC-20 tokens within team and project dashboards.
  - Added a multi-step token creation flow with form validation, including token info, distribution, sale settings, airdrop CSV upload, and launch process with real-time status tracking.
  - Implemented visual token allocation charts and multi-step progress indicators for enhanced user experience.
  - Added a dedicated "Assets" section in the sidebar with a "New" badge.
  - Added a horizontal segmented distribution bar chart component for visualizing token allocations.
  - Introduced a multi-step status component to track asynchronous step executions with retry capabilities.
  - Added token airdrop CSV upload and validation feature with user-friendly UI and error handling.
  - Added token sale configuration section with price and allocation inputs.
  - Enhanced import modal to support both contract and asset imports with contextual UI and behavior.
  - Added new pages and components to support token asset creation and management workflows, including cards for asset actions.
  - Added server-side path revalidation support for pages and layouts.

- **Improvements**
  - Enhanced contract and asset import workflows with clearer UI, context-aware dialogs, and improved contract table variant handling.
  - Updated number input fields to remove browser-native spinners for a cleaner appearance.
  - Improved analytics event tracking for user interactions and deployment steps.
  - Refined UI components for consistent styling and accessibility.
  - Fixed sidebar badge placement and updated UI text for clarity.
  - Updated dialogs and payment embeds to reference transaction chain instead of active wallet chain and set default payment amounts.
  - Changed token supply label to "Circulating Supply" for clarity.
  - Replaced Chakra UI spinner and styled elements with local components for simplified UI.

- **Bug Fixes**
  - Corrected UI typos and improved analytics event tracking for deployment errors.

- **Documentation**
  - Added Storybook stories for new components to facilitate UI testing and documentation.

- **Chores**
  - Refactored and extended types and props to support new asset and contract features, ensuring future extensibility.
  - Added tracking utilities for token deployment and asset creation analytics.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@graphite-app graphite-app bot force-pushed the 05-20-_tool-4531_dashboard_add_token_asset_creation_wizard branch from 24a556b to 73629e9 Compare May 22, 2025 23:15
@vercel vercel bot temporarily deployed to Preview – thirdweb_playground May 22, 2025 23:15 Inactive
@vercel vercel bot temporarily deployed to Preview – docs-v2 May 22, 2025 23:15 Inactive
@vercel vercel bot temporarily deployed to Preview – login May 22, 2025 23:15 Inactive
@vercel vercel bot temporarily deployed to Preview – wallet-ui May 22, 2025 23:15 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (3)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx (3)

83-85: Add form validation before proceeding to next step

Currently, the code transitions to the next step without validating that the current form is valid. This could lead to incomplete or invalid data being used later in the flow.

onNext={() => {
+   // Validate form before proceeding
+   tokenInfoForm.trigger().then((isValid) => {
+     if (isValid) {
        setStep("distribution");
+     }
+   });
}}

97-99: Add form validation before proceeding to launch step

Similar to the previous step transition, there's no validation before moving to the launch step.

onNext={() => {
+   // Validate form before proceeding
+   tokenDistributionForm.trigger().then((isValid) => {
+     if (isValid) {
        setStep("launch");
+     }
+   });
}}

111-114: Validate combined form data before passing to LaunchTokenStatus

When combining form data for the launch step, no validation is performed to ensure the complete dataset is valid.

{step === "launch" && (
  <LaunchTokenStatus
    client={props.client}
    onLaunchSuccess={props.onLaunchSuccess}
    onPrevious={() => {
      setStep("distribution");
    }}
    createTokenFunctions={props.createTokenFunctions}
+   // Ensure both forms are valid before proceeding
+   isValid={tokenInfoForm.formState.isValid && tokenDistributionForm.formState.isValid}
+   form={{
+     tokenInfoForm,
+     tokenDistributionForm,
+   }}
    values={{
      ...tokenInfoForm.getValues(),
      ...tokenDistributionForm.getValues(),
    }}
  />
)}

Then in LaunchTokenStatus component, add validation before beginning the launch sequence.

🧹 Nitpick comments (2)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx (2)

3-6: Remove unused empty imports

There are several empty imports that don't bring any symbols into scope:

-import {} from "@/components/blocks/multi-step-status/multi-step-status";
-import {} from "@/components/ui/dialog";
-import {} from "lucide-react";
+// Remove unused imports

These should either be removed entirely or replaced with actual imports if the components are needed elsewhere in the file.


40-60: Consider adding field labels and more descriptive initial values

The initial form values are mostly empty strings, which doesn't provide guidance to users about expected formats. Consider adding either:

  1. More descriptive placeholder values that show format examples
  2. Default values that make sense for common use cases
  3. Tooltip descriptions for each field

This would improve the user experience by making it clearer what information should be entered in each field.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24a556b and 73629e9.

📒 Files selected for processing (36)
  • apps/dashboard/src/@/actions/revalidate.ts (1 hunks)
  • apps/dashboard/src/@/components/blocks/distribution-chart.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.stories.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/_components/DeployedContractsPage.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/_components/getProjectContracts.ts (1 hunks)
  • apps/dashboard/src/app/(app)/account/contracts/_components/getSortedDeployedContracts.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/cards.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-card.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page-impl.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.stories.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-airdrop.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-distribution.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-sale.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/form.ts (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/tracking.ts (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/components/ProjectSidebarLayout.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/connect/account-abstraction/factories/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/hooks/project-contracts.ts (1 hunks)
  • apps/dashboard/src/components/buttons/MismatchButton.tsx (2 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/import-contract/modal.tsx (7 hunks)
  • apps/dashboard/src/components/contract-components/tables/contract-table.stories.tsx (6 hunks)
  • apps/dashboard/src/components/contract-components/tables/contract-table.tsx (6 hunks)
  • apps/dashboard/src/contract-ui/components/solidity-inputs/address-input.tsx (3 hunks)
  • apps/dashboard/src/global.css (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (35)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/supply-layout.tsx
  • apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx
  • apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/connect/account-abstraction/factories/page.tsx
  • apps/dashboard/src/app/(app)/account/contracts/_components/DeployedContractsPage.tsx
  • apps/dashboard/src/global.css
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx
  • apps/dashboard/src/@/actions/revalidate.ts
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/components/ProjectSidebarLayout.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/hooks/project-contracts.ts
  • apps/dashboard/src/components/contract-components/tables/contract-table.stories.tsx
  • apps/dashboard/src/app/(app)/account/contracts/_components/getSortedDeployedContracts.tsx
  • apps/dashboard/src/app/(app)/account/contracts/_components/getProjectContracts.ts
  • apps/dashboard/src/components/buttons/MismatchButton.tsx
  • apps/dashboard/src/contract-ui/components/solidity-inputs/address-input.tsx
  • apps/dashboard/src/@/components/blocks/distribution-chart.tsx
  • apps/dashboard/src/components/contract-components/tables/contract-table.tsx
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx
  • apps/dashboard/src/components/contract-components/import-contract/modal.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.stories.tsx
  • apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.stories.tsx
  • apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-card.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-sale.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-airdrop.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/page.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/distribution/token-distribution.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/cards.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/tracking.ts
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/page.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/launch/launch-token.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page-impl.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/form.ts
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/token-info-fieldset.tsx
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Unit Tests
  • GitHub Check: Size
  • GitHub Check: Lint Packages
  • GitHub Check: Analyze (javascript)

Comment on lines +62 to +75
const tokenDistributionForm = useForm<TokenDistributionFormValues>({
resolver: zodResolver(tokenDistributionFormSchema),
values: {
// sale fieldset
saleAllocationPercentage: "0",
salePrice: "0.1",
supply: "1000000",
saleEnabled: false,
// airdrop
airdropEnabled: false,
airdropAddresses: [],
},
reValidateMode: "onChange",
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add input validation for numerical values

The form includes numerical values like saleAllocationPercentage, salePrice, and supply that are currently stored as strings. While Zod validation likely handles this, consider adding:

  1. Proper type conversion in the form
  2. Min/max constraints for percentage values (0-100)
  3. Minimum values for price and supply to prevent invalid or negative values

This would provide better guidance to users and prevent errors during token creation.

🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx
between lines 62 and 75, the form fields for numerical values like
saleAllocationPercentage, salePrice, and supply are currently initialized as
strings without explicit input validation or type conversion. Update the form to
convert these fields to appropriate numeric types on input, enforce min/max
constraints such as 0 to 100 for saleAllocationPercentage, and set minimum
allowed values for salePrice and supply to prevent negative or invalid inputs.
This involves adjusting the form initialization values, adding validation rules
or schema refinements, and ensuring the UI input components reflect these
constraints for better user guidance and data integrity.

Comment on lines +77 to +118
return (
<div>
{step === "token-info" && (
<TokenInfoFieldset
client={props.client}
form={tokenInfoForm}
onNext={() => {
setStep("distribution");
}}
/>
)}

{step === "distribution" && (
<TokenDistributionFieldset
form={tokenDistributionForm}
accountAddress={props.accountAddress}
chainId={tokenInfoForm.watch("chain")}
onPrevious={() => {
setStep("token-info");
}}
onNext={() => {
setStep("launch");
}}
/>
)}

{step === "launch" && (
<LaunchTokenStatus
client={props.client}
onLaunchSuccess={props.onLaunchSuccess}
onPrevious={() => {
setStep("distribution");
}}
createTokenFunctions={props.createTokenFunctions}
values={{
...tokenInfoForm.getValues(),
...tokenDistributionForm.getValues(),
}}
/>
)}
</div>
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add loading states during form transitions

The component doesn't manage loading states during step transitions or form submissions. Consider adding:

export function CreateTokenAssetPageUI(props: {
  accountAddress: string;
  client: ThirdwebClient;
  createTokenFunctions: CreateTokenFunctions;
  onLaunchSuccess: () => void;
}) {
  const [step, setStep] = useState<"token-info" | "distribution" | "launch">(
    "token-info",
  );
+ const [isLoading, setIsLoading] = useState(false);

  // ... rest of code

  return (
    <div>
      {step === "token-info" && (
        <TokenInfoFieldset
          client={props.client}
          form={tokenInfoForm}
+         isLoading={isLoading}
          onNext={() => {
+           setIsLoading(true);
            // Validate form before proceeding
            tokenInfoForm.trigger().then((isValid) => {
              if (isValid) {
                setStep("distribution");
              }
+             setIsLoading(false);
            });
          }}
        />
      )}
      
      // ... apply similar changes to other steps

This would provide visual feedback to users during transitions and prevent multiple submissions.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create/create-token-page.client.tsx
between lines 77 and 118, the component lacks loading state management during
step transitions and form submissions. Add a loading state variable to track
when a transition or submission is in progress, update this state appropriately
in the onNext and onPrevious handlers, and conditionally render a loading
indicator or disable buttons to provide visual feedback and prevent multiple
submissions.

@graphite-app graphite-app bot merged commit 73629e9 into main May 22, 2025
24 checks passed
@graphite-app graphite-app bot deleted the 05-20-_tool-4531_dashboard_add_token_asset_creation_wizard branch May 22, 2025 23:23
@vercel vercel bot temporarily deployed to Production – docs-v2 May 22, 2025 23:23 Inactive
@vercel vercel bot temporarily deployed to Production – wallet-ui May 22, 2025 23:23 Inactive
@vercel vercel bot temporarily deployed to Production – thirdweb_playground May 22, 2025 23:23 Inactive
@vercel vercel bot temporarily deployed to Production – login May 22, 2025 23:23 Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Dashboard Involves changes to the Dashboard.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants